package com.scalar.chiptrack.wipProcessView;

import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;

/**
 * User: Sujan
 * Date: May 5, 2005
 * Time: 1:50:26 PM
 */
public class GraphViewPanel extends JPanel
{
	private ProcessViewGraph processViewGraph;
	private int defaultWidth 			= 700;
	private int defaultHeight 			= 800;
	private int graphWidth 				= 0;
	private int graphHeight 			= 0;
	private int graphWidthGap 			= 40;
	private int graphHeightGap 			= 20;

	private final int LEFTDOWNLEFT 		= 1;
	private final int SIMPLEDOWNEDGE 	= 2;
	private final int HORIZONTAL 		= 3;
	private final int RIGHTDOWNLEFT 	= 4;
	private final int RIGHTDOWNRIGHT 	= 5;
	private final int SIMPLEUPEDGE 		= 6;
	private final int LEFTUPRIGHT		= 7;
	private final int DOWNLEFTUP		= 8;
	private final int DOWNRIGHTUP 		= 9;

	private GraphViewPanel()
	{

	}
	public GraphViewPanel ( ProcessViewGraph graph )
	{
		this.processViewGraph = graph;
		init();
	}
    public void init ( )
	{
		setLayout ( null );
		placeNodes ();
		Dimension preferredDimension = new Dimension ( graphWidth + graphWidthGap , graphHeight + graphHeightGap );
		setPreferredSize ( preferredDimension );
		setMaximumSize ( preferredDimension );
	}
	public void placeNodes ()
	{
		JLabel nodeLabel = null;
		Node node = null;
		if ( processViewGraph != null )
		{
			NodeList nodeList = processViewGraph.getNodeList ();
			int nodeCount = ( nodeList == null ) ? 0 : nodeList.size();
			for ( int i=0; i < nodeCount; i++ )
			{
				node = ( Node )nodeList.get ( i );
				nodeLabel = new JLabel ( node.getNodeLabel () )
				{
					public Point getToolTipLocation ( MouseEvent event )
					{
						return new Point ( event.getX () + 20, event.getY()+30 );	
					}
				};
				nodeLabel.setToolTipText ( node.getLotInfo () );
				// Keep the tool tip showing
    			int dismissDelay = Integer.MAX_VALUE;
    			ToolTipManager.sharedInstance().setDismissDelay(dismissDelay);
				nodeLabel.setBounds ( node.absX,node.y,node.getWidth (),node.getHeight () );
				nodeLabel.setHorizontalAlignment(JLabel.CENTER);
				nodeLabel.setBorder(BorderFactory.createLineBorder ( Color.BLUE ));
				if ( ( node.absX + node.getWidth () ) > graphWidth )
				{
					graphWidth = node.absX + node.getWidth();
				}
				if ( ( node.y + node.getHeight() ) > graphHeight )
				{
					graphHeight = node.y + node.getHeight ();
				}
				add ( nodeLabel );
			}
		}

	}
	public void drawEdges ( )
	{
		//repaint();
	}
	public void paintComponent ( Graphics g )
	{
		super.paintComponent(g);
		setBackground ( Color.white);
		if ( processViewGraph != null )
		{
			EdgeList edgeList = processViewGraph.getEdgeList ();
			int edgeListCount = ( edgeList == null ) ? 0 : edgeList.size();
			int fromNodeIdx = -1;
			int toNodeIdx = -1;
			Node fromNode = null;
			Node toNode = null;
			for ( int i=0; i < edgeListCount; i++ )
			{
				Edge edge = ( Edge ) edgeList.get ( i );
				fromNodeIdx = edge.getFrom ();
				toNodeIdx = edge.getTo();
				fromNode = processViewGraph.getNodeAt ( fromNodeIdx );
				toNode	= processViewGraph.getNodeAt ( toNodeIdx );
				drawEdge ( g, fromNode, toNode );
			}
		}
	}
	public void drawSimpleEdge ( Graphics g, Node fromNode, Node toNode )
	{
	 	int x1 = ( fromNode == null ) ? -1 : fromNode.absX + fromNode.getWidth ()/2;
		int y1 = ( fromNode == null ) ? -1 : fromNode.y + fromNode.getHeight ();

		int x2 = ( toNode == null ) ? -1 : toNode.absX + toNode.getWidth ()/2;
		int y2 = ( toNode == null ) ? -1 : toNode.y;
		DrawArrow.drawArrow(g, x1, y1, x2, y2 );
	}
	public void drawSimpleUpEdge ( Graphics g, Node fromNode, Node toNode )
	{
		int x1 = ( fromNode == null ) ? -1 : fromNode.absX + fromNode.getWidth ()/2;
		int y1 = ( fromNode == null ) ? -1 : fromNode.y;

		int x2 = ( toNode == null ) ? -1 : toNode.absX + toNode.getWidth ()/2;
		int y2 = ( toNode == null ) ? -1 : toNode.y + toNode.getHeight();
		DrawArrow.drawArrow ( g, x1, y1, x2, y2 );
	}
	public void drawEdge ( Graphics g, Node fromNode, Node toNode )
	{
		int x1 = fromNode.absX;
		int y1 = fromNode.y;
		int x2 = toNode.absX;
		int y2 = toNode.y;
		int edgeType = getEdgeType( fromNode, toNode );
		switch ( edgeType )
		{
			case HORIZONTAL:
				{
					x1 = fromNode.absX ;
					y1 = fromNode.y + fromNode.getHeight ()/2;

					x2 = toNode.absX + toNode.getWidth ();
					y2 = toNode.y + toNode.getHeight ()/2;
					DrawArrow.drawArrow(g, x1, y1, x2, y2 );
					break;
				}
			case LEFTDOWNLEFT:
				{
					Rectangle interNodeArea = new Rectangle ( toNode.absX+toNode.getWidth ()+1,fromNode.y, fromNode.absX - (toNode.absX + toNode.getWidth()+2), toNode.y + toNode.getHeight () -  - fromNode.y );
					x1 = fromNode.absX;
					y1 = fromNode.y + fromNode.getHeight ()/2;

					x2 = toNode.absX + toNode.getWidth ();
					y2 = toNode.y + toNode.getHeight ()/2;

					int tempX1 = x1 - ( (int)interNodeArea.getWidth ()/2 - 3 );
					int tempY1 = y1;

					int tempX2 = tempX1;
					int tempY2 = y2;

					DrawArrow.drawArrow(g, x1, y1, tempX1, tempY1 );
					DrawArrow.drawArrow(g, tempX1, tempY1, tempX2, tempY2 );
					DrawArrow.drawArrow(g, tempX2, tempY2, x2, y2 );

					break;
				}
			case RIGHTDOWNLEFT:
				{
					x1 = fromNode.absX + fromNode.getWidth ();
					y1 = fromNode.y + fromNode.getHeight()/2;

					x2 = toNode.absX + toNode.getWidth();
					y2 = toNode.y + toNode.getHeight()/2;

					int tempX1 = x1 + processViewGraph.getNodeDistance ()/2 - 3;
					int tempY1 = y1;

					int tempX2 = x2 + processViewGraph.getNodeDistance ()/2 - 5;
					int tempY2 = y2;

                    DrawArrow.drawArrow(g, x1, y1, tempX1, tempY1 );
					DrawArrow.drawArrow(g, tempX1, tempY1, tempX2, tempY2 );
					DrawArrow.drawArrow(g, tempX2, tempY2, x2, y2 );

					break;
				}
			case RIGHTDOWNRIGHT:
				{
					Rectangle interNodeArea = new Rectangle ( fromNode.absX + fromNode.getWidth () + 1, fromNode.y, toNode.absX - ( fromNode.absX + fromNode.getWidth() + 2 ), toNode.y + toNode.getHeight () - fromNode.y );
					x1 = fromNode.absX + fromNode.getWidth();
					y1 = fromNode.y + fromNode.getHeight ()/2;

					x2 = toNode.absX ;
					y2 = toNode.y + toNode.getHeight ()/2;

					int tempX1 = x1 + ( (int)interNodeArea.getWidth ()/2 - 3 );
					int tempY1 = y1;

					int tempX2 = tempX1 + 1;
					int tempY2 = y2;

					DrawArrow.drawArrow(g, x1, y1, tempX1, tempY1 );
					DrawArrow.drawArrow(g, tempX1, tempY1, tempX2, tempY2 );
					DrawArrow.drawArrow(g, tempX2, tempY2, x2, y2 );

					break;
				}
			case DOWNRIGHTUP:
			case DOWNLEFTUP:
				{
					x1 = fromNode.absX + fromNode.getWidth ()/2;
					y1 = fromNode.y + fromNode.getHeight ();

					x2 = toNode.absX + toNode.getWidth ()/2;
					y2 = toNode.y + toNode.getHeight ();

					int tempX1 = x1;
					int tempY1 = y1 + ( processViewGraph.getVerticalDistance ()/2 - 2 );

					int tempX2 = x2;
					int tempY2 = tempY1 -1;

					DrawArrow.drawArrow(g, x1, y1, tempX1, tempY1 );
					DrawArrow.drawArrow(g, tempX1, tempY1, tempX2, tempY2 );
					DrawArrow.drawArrow(g, tempX2, tempY2, x2, y2 );
					break;
				}
			case SIMPLEDOWNEDGE:
				{
					drawSimpleEdge ( g, fromNode, toNode );
					break;
				}
			case SIMPLEUPEDGE:
				{
					drawSimpleUpEdge ( g, fromNode, toNode );
					break;
				}
			case LEFTUPRIGHT:
				{
					x1 = fromNode.absX;
					y1 = fromNode.y + fromNode.getHeight ()/2;

					x2 = toNode.absX;
					y2 = toNode.y + toNode.getHeight ()/2;

					int tempX1 = x1 - ( processViewGraph.getNodeDistance ()/2 - 3 );
					int tempY1 = y1;

					int tempX2 = tempX1 + 1;
					int tempY2 = y2;

					DrawArrow.drawArrow ( g, x1, y1, tempX1, tempY1 );
					DrawArrow.drawArrow ( g, tempX1, tempY1, tempX2, tempY2 );
					DrawArrow.drawArrow ( g, tempX2, tempY2, x2, y2 );
					break;
				}
			default:
				drawSimpleEdge ( g, fromNode, toNode );
		}
	}
	private int getEdgeType ( Node fromNode, Node toNode )
	{
		int x1 = fromNode.absX;
		int y1 = fromNode.y;
		int level1 = fromNode.getLevel();

		int x2 = toNode.absX;
		int y2 = toNode.y;
		int level2 = toNode.getLevel();

		int edgeType = 0;

		if ( level1 == level2 )
		{
			if ( x1 > x2 ) //left to right
			{
				boolean overlap = false;
				Rectangle interNodeArea = new Rectangle ( toNode.absX+toNode.getWidth ()+1,toNode.y, fromNode.absX - (toNode.absX + toNode.getWidth()+2), toNode.getHeight () );
				NodeList levelNodes = processViewGraph.getNodesAtLevel ( level1 );
				for ( int k=0; k < levelNodes.size(); k++ )
				{
					Node levelNode = levelNodes.getNodeAt ( k );
					if ( interNodeArea.intersects ( levelNode.getBounds () ) )
					{
						overlap = true;
						break;
					}
				}
				if ( !overlap )
				{
					edgeType = HORIZONTAL;
				}
				else
				{
					edgeType = DOWNLEFTUP;
				}
			}
			else
			{
			 	boolean overlap = false;
				Rectangle interNodeArea = new Rectangle ( fromNode.absX+fromNode.getWidth ()+1,fromNode.y, toNode.absX - (fromNode.absX + fromNode.getWidth()+2), toNode.getHeight () );
				NodeList levelNodes = processViewGraph.getNodesAtLevel ( level1 );
				for ( int k=0; k < levelNodes.size(); k++ )
				{
					Node levelNode = levelNodes.getNodeAt ( k );
					if ( interNodeArea.intersects ( levelNode.getBounds () ) )
					{
						overlap = true;
						break;
					}
				}
				if ( !overlap )
				{
					edgeType = HORIZONTAL;
				}
				else
				{
					edgeType = DOWNRIGHTUP;
				}
			}
		}
		else if ( ( level1 + 1 ) < level2 )
		{
			if ( x1 == x2 )
			{
				edgeType = RIGHTDOWNLEFT;
			}
			else if ( ( x1 > ( x2 + toNode.getWidth() ) ) || ( x1 + (fromNode.getWidth() ) ) < x2 ) //No common x position
			{
				if ( x1 > x2 ) //right to left
				{
					Rectangle interNodeArea = new Rectangle ( toNode.absX+toNode.getWidth ()+1,fromNode.y, fromNode.absX - (toNode.absX + toNode.getWidth()+2), toNode.y + toNode.getHeight () -  - fromNode.y );
					boolean overlap = false;
					for ( int level = fromNode.getLevel(); level <= toNode.getLevel(); level++ )
					{
						NodeList levelNodes = processViewGraph.getNodesAtLevel ( level );
						for ( int k=0; k < levelNodes.size(); k++ )
						{
							Node levelNode = levelNodes.getNodeAt ( k );
							if ( interNodeArea.intersects ( levelNode.getBounds () ) )
							{
								overlap = true;
								break;
							}
						}
						if ( overlap )
						{
							break;
						}
					}
					if ( !overlap )
					{
						edgeType = LEFTDOWNLEFT;
					}
					else
					{
						edgeType = SIMPLEDOWNEDGE;
					}
				}
				else  //left to right
				{
					Rectangle interNodeArea = new Rectangle ( fromNode.absX + fromNode.getWidth () + 1, fromNode.y, toNode.absX - ( fromNode.absX + fromNode.getWidth() + 2 ), toNode.y + toNode.getHeight () - fromNode.y );
					boolean overlap = false;
					for ( int level = fromNode.getLevel(); level <= toNode.getLevel(); level++ )
					{
						NodeList levelNodes = processViewGraph.getNodesAtLevel ( level );
						for ( int k=0; k < levelNodes.size(); k++ )
						{
							Node levelNode = levelNodes.getNodeAt ( k );
							if ( interNodeArea.intersects ( levelNode.getBounds () ) )
							{
								overlap = true;
								break;
							}
						}
						if ( overlap )
						{
							break;
						}
					}
					if ( !overlap )
					{
						edgeType = RIGHTDOWNRIGHT;
					}
					else
					{
						edgeType = SIMPLEDOWNEDGE;
					}
				}

			}
			else  //common x position
			{
				edgeType = SIMPLEDOWNEDGE; //todo
			}
		}
		else if ( level1 > level2 ) //UP ARROW
		{
			if ( x1 == x2 )
			{
				if ( ( level2+1 ) == level1 )
				{
					edgeType = SIMPLEUPEDGE;
				}
				else
				{
					edgeType = LEFTUPRIGHT;
				}
			}
			else
			{
				edgeType = SIMPLEUPEDGE;//todo
			}
		}
		else
		{
			edgeType = SIMPLEDOWNEDGE;
		}
		return edgeType;
	}
}
